import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;


public class TripleDb {

	private static TripleDb tripleDb;
	private Map<String,Map<String, Set<String>>> db; 
	private String dbfile;
	
	public static TripleDb getTripleDb() 
	{
		if(tripleDb == null)
			tripleDb = new TripleDb();
		
		/*Load the database */
		try{
			if(tripleDb.dbfile == null)  /* Only load it once. We use dbfilename as a kind of flag.*/
				tripleDb.loadDB();
		}catch(IOException ioe)
		{
			Logger.getLogger().error("Error in loading dbfile: " + tripleDb.dbfile + " " + ioe );
		}
		
		return tripleDb;
	}
	
	private void loadDB() throws IOException
	{
		ConfigurationManager configmanager = ConfigurationManager.getPropertyManager();
		dbfile = configmanager.getDBFile();
		
		BufferedReader reader = new BufferedReader(new FileReader(dbfile));
		
		/*Load db into memory*/
		String str;
		while( (str = reader.readLine()) != null)
		{
			List<String> strList = ServerThread.sanitize(str.split(" "));
			if(strList.size() != 3) /* Oh oh Major Goof up ! */
			{
				Logger.getLogger().error("Triple Size != 3 " + strList + " Str:"+ str);
			}
			add(strList.get(0),strList.get(1),strList.get(2));
		}
		
		reader.close();
	}
	
	private TripleDb()
	{
		db = new HashMap<String, Map<String,Set<String>>>();
	}
	
	public synchronized void add(String mainkey, String subkey, String value)
	{
		Map<String, Set<String>> subkeyMap;
		Set<String> set;
		
		subkeyMap = db.get(mainkey);
		if(subkeyMap == null)
		{
			/* New Entry. */
			subkeyMap = new HashMap<String, Set<String>>();
			set= new HashSet<String>();
			
			set.add(value);
			subkeyMap.put(subkey, set);
			db.put(mainkey, subkeyMap);
			return;
		}
		
		set = subkeyMap.get(subkey);
		if(set == null)
		{
			/* Shouldnt occur, but lets deal with it anyway. */
			set = new HashSet<String>();
			set.add(value);
			subkeyMap.put(subkey, set);
			db.put(mainkey, subkeyMap);
			return;
		}
		
		/*Otherwise just add to set.*/
		set.add(value);
		return;
	}
	
	public synchronized boolean remove(String mainkey, String subkey, String val)
	{
		Map<String, Set<String>> subkeyMap;
		Set<String> set;
		
		subkeyMap = db.get(mainkey);
		
		if(subkeyMap == null)
			return false;
		
		set = subkeyMap.get(subkey);
		
		if(set == null)
			return false;
		
		return set.remove(val);
	}
	
	private List<String> getQueryResults(String mainkey, String subkey, Set<String> set)
	{
		List<String> result = new ArrayList<String>();
		
		
		
		for(String val: set )
		{
			result.add(mainkey +" " + subkey + " " + val);
		}
		
		return result;
	}
	
	public synchronized List<String> query(String mainkey, String subkey)
	{
		List<String> result = new ArrayList<String>();
		Map<String, Set<String>> subkeyMap;
		Set<String> set;
		
		subkeyMap = db.get(mainkey);
		
		if(subkeyMap == null)
			return result; 
		
		if(subkey.equalsIgnoreCase("*"))
		{
			for(String setkey: subkeyMap.keySet())
			{
				set = subkeyMap.get(setkey);
				result.addAll(getQueryResults(mainkey, setkey, set));
			}
			
			return result;
		}
		
		set = subkeyMap.get(subkey);
		result.addAll(getQueryResults(mainkey, subkey, set));
		return result;
	}
	
	public void saveDB() throws IOException
	{
		BufferedWriter writer = new BufferedWriter(new FileWriter(dbfile));
		
		for(String mainKey : db.keySet())
		{
			Map<String, Set<String>> subkeyMap = db.get(mainKey);
			for(String setKey: subkeyMap.keySet())
			{
				Set<String> valSet = subkeyMap.get(setKey);
				for(String val: valSet)
				{
					writer.write(mainKey + " " + setKey + " " + val+"\n");
				}
			}
		}
		
		writer.flush();
		writer.close();
	}
	
}
